package com.glsc.ngateway.platform.service.owncloud;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.alibaba.excel.util.CollectionUtils;
import com.glsc.ngateway.common.api.oaflow.dto.SystemFlowStatisticsSearchParamDto;
import com.glsc.ngateway.common.api.platform.dto.user.PlatformUserDto;
import com.glsc.ngateway.common.base.domain.mysql.gateway.other.SystemConfig;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.OwnCloudSystemDirConfig;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.it.ItDict;
import com.glsc.ngateway.common.base.domain.mysql.gateway.system.SystemMain;
import com.glsc.ngateway.common.base.exception.PlatformException;
import com.glsc.ngateway.common.base.repo.mysql.gateway.platform.OwnCloudSystemDirConfigRepo;
import com.glsc.ngateway.common.base.utils.TreeNodeUtils;
import com.glsc.ngateway.common.base.utils.redis.IbltRedis;
import com.glsc.ngateway.common.base.utils.redis.RedisKey;
import com.glsc.ngateway.common.oc.domain.OcFilecache;
import com.glsc.ngateway.common.oc.dto.QuerySystemDocTreeByFileidDto;
import com.glsc.ngateway.common.oc.enums.OcFilecacheMimetypeEnum;
import com.glsc.ngateway.common.oc.repo.OcFilecacheRepository;
import com.glsc.ngateway.platform.service.SystemConfigService;
import com.glsc.ngateway.platform.service.it.DictionaryService;
import com.glsc.ngateway.platform.service.system.SystemMainService;
import com.glsc.ngateway.platform.utils.RequestTool;
import com.glsc.ngateway.platform.utils.owncloud.OwncloudUtils;
import com.glsc.ngateway.platform.vo.SystemRelatedFileConfigVo;
import com.glsc.ngateway.platform.vo.SystemRelatedFileInfoVo;
import lombok.*;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.DataBinder;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Nullable;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotBlank;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.http.HttpResponse;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 网盘服务类
 *
 * @author xuchang
 */
@Service
public class OwnCloudService {
    private static Logger logger = LoggerFactory.getLogger(OwnCloudService.class);
    private static final BigDecimal SIZE_KB = new BigDecimal(1024);
    private static final BigDecimal SIZE_MB = SIZE_KB.multiply(SIZE_KB);
    private static final BigDecimal SIZE_GB = SIZE_KB.multiply(SIZE_KB).multiply(SIZE_KB);
    private static final BigDecimal SIZE_TB = SIZE_KB.multiply(SIZE_KB).multiply(SIZE_KB).multiply(SIZE_KB);
    @Resource
    private SystemConfigService systemConfigService;
    @Resource
    private OcFilecacheRepository ocFilecacheRepo;
    @Resource
    private RequestTool requestTool;
    @Resource
    private OwnCloudSystemDirConfigRepo systemDirConfigRepo;
    @Resource
    private SystemMainService systemMainService;
    @Resource
    private DictionaryService dictionaryService;
    @Resource
    private IbltRedis ibltRedis;
    private ThreadPoolExecutor ocThreadPool = new ThreadPoolExecutor(
            5, 5,
//            Runtime.getRuntime().availableProcessors() * 2,
//            Runtime.getRuntime().availableProcessors() * 2,
            0,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(),
            new ThreadFactoryBuilder()
                    .setNamePrefix("MyThread-%d")
                    .build(),
            new ThreadPoolExecutor.AbortPolicy()
    );
    //系统文档根目录对应owncloud的fileid
    public static final String CONFIG_KEY_SYSTEM_DOC_ROOT_FILEID = "CONFIG_KEY_SYSTEM_DOC_ROOT_FILEID";
    //系统文档特殊处理的目录对应owncloud的fileid
    public static final String CONFIG_KEY_SYSTEM_DOC_EX_FILEID = "CONFIG_KEY_SYSTEM_DOC_EX_FILEID";
    //系统文档特殊处理的目录对应owncloud的file_name
    public static final String CONFIG_KEY_SYSTEM_DOC_EX_FILENAME = "CONFIG_KEY_SYSTEM_DOC_EX_FILENAME";
    //系统文档下载根目录替换
    public static final String CONFIG_KEY_SYSTEM_DOC_PATH_REPLACE = "CONFIG_KEY_SYSTEM_DOC_PATH_REPLACE";
    public static final Set<String> PRE_DIR = new HashSet<>() {{
        add(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_1ST);
        add(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_2ND);
        add(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_3RD);
    }};

    public static final String OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_1ST = "/系统大类";
    public static final String OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_2ND = "/系统大类/系统类别";
    public static final String OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_3RD = "/系统大类/系统类别/系统";
    //    public static final String OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL = "files/01_系统/系统文件（新）/";
    public static final String OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL = "OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL";
    //    public static final String OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_DB = "files/信息技术总部/01_系统文档/01_系统/系统文件（新）/";
    public static final String OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_DB = "OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_DB";
    public static final String OWNCLOUD_SYSTEM_DOC_SYS_DIR_PATH_TMP = "临时系统目录";

    private final Pattern SYSTEM_OPNAME_MATCH_REG_EX = Pattern.compile("[!@#$%^&*+={};':\"\\\\|,/?" + "~]+");

    public SystemRelatedFileInfoVo queryByName(@NotBlank String opName, @Nullable String fileName) {
        SystemConfig rootConfig = systemConfigService.findByCode(CONFIG_KEY_SYSTEM_DOC_ROOT_FILEID);
        List<Long> rootFileids = Arrays.stream(Optional.ofNullable(rootConfig).orElseThrow(() -> PlatformException.error("网盘浏览异常-缺少配置：系统文档根目录对应owncloud的fileid-" + CONFIG_KEY_SYSTEM_DOC_ROOT_FILEID)).getConfigValue()
                .split(",")).map(Long::valueOf).collect(Collectors.toList());
        SystemConfig exConfig = systemConfigService.findByCode(CONFIG_KEY_SYSTEM_DOC_EX_FILEID);
        List<Long> exFileids = Objects.nonNull(exConfig) ? Arrays.stream(exConfig.getConfigValue().split(",")).map(Long::valueOf).collect(Collectors.toList()) : null;
        SystemConfig exNameConfig = systemConfigService.findByCode(CONFIG_KEY_SYSTEM_DOC_EX_FILENAME);
        List<String> exFileNames = Objects.nonNull(exNameConfig) ? Arrays.stream(exNameConfig.getConfigValue().split(",")).collect(Collectors.toList()) : null;
        //先查系统下所有，如果搜索文件名，则先查
        List<Map<String, Object>> maps = ocFilecacheRepo.queryByFileidAndName(rootFileids, exFileids, opName,
                Strings.isNotBlank(fileName) ? OcFilecacheMimetypeEnum.dir.getCode() : null, null, exFileNames);
        //系统文件路径（新）
        SystemMain systemMain = Strings.isNotBlank(opName) ? systemMainService.findByOpName(opName) : null;
        String sysDirNew = Objects.nonNull(systemMain) ? getPathByOpName(systemMain, OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_DB)) : null;
        String opNameNew = Strings.isNotBlank(opName) ? (systemMain.getSystemClassNo() + " " + systemMain.getOpName()) : null;
        List<Map<String, Object>> maps1 = ocFilecacheRepo.queryByFileidAndNameNew(sysDirNew, opNameNew, Strings.isNotBlank(fileName) ? OcFilecacheMimetypeEnum.dir.getCode() : null, null, exFileNames);
        maps.addAll(maps1);
        List<QuerySystemDocTreeByFileidDto> allDirList = maps
                .stream()
                .map(q -> {
                    QuerySystemDocTreeByFileidDto d = new QuerySystemDocTreeByFileidDto();
                    DataBinder binder = new DataBinder(d);
                    binder.bind(new MutablePropertyValues(q));
                    return d;
                })
                .collect(Collectors.toList());
        SystemConfig replacePathConfig = systemConfigService.findByCode(CONFIG_KEY_SYSTEM_DOC_PATH_REPLACE);
        if (Objects.nonNull(replacePathConfig) && Strings.isNotBlank(replacePathConfig.getConfigValue())) {
            Map<String, String> replaceMap = Arrays.stream(replacePathConfig.getConfigValue().split(",")).collect(Collectors.toMap(c -> c.split(":")[0], c -> c.split(":")[1]));
            allDirList.forEach(d -> {
                replaceMap.forEach((k, v) -> {
                    d.setPath(d.getPath().replaceFirst(k, v));
                });
            });
        }
        logger.info("系统关联文档查询，系统文件夹路径{}，路径数量:{}", opName, allDirList.size());
        //如果查文件名，根据文件名筛选
        if (Strings.isNotBlank(fileName)) {
            List<Map<String, Object>> maps2 = ocFilecacheRepo.queryByFileidAndName(rootFileids, exFileids, opName, null, fileName, exFileNames);
            List<Map<String, Object>> maps3 = ocFilecacheRepo.queryByFileidAndNameNew(sysDirNew, opNameNew, Strings.isNotBlank(fileName) ? OcFilecacheMimetypeEnum.dir.getCode() : null, fileName, exFileNames);
            maps2.addAll(maps3);
            List<QuerySystemDocTreeByFileidDto> queryByFileName = maps2
                    .stream()
                    .map(q -> {
                        QuerySystemDocTreeByFileidDto d = new QuerySystemDocTreeByFileidDto();
                        DataBinder binder = new DataBinder(d);
                        binder.bind(new MutablePropertyValues(q));
                        return d;
                    })
                    .collect(Collectors.toList());
            logger.info("系统关联文档查询，搜索系统{}，文件，{}， 数量{}", opName, fileName, queryByFileName.size());
            if (CollectionUtil.isEmpty(queryByFileName)) {
                return SystemRelatedFileInfoVo.EMPTY_LIST;
            } else {
                //自下而上搜索路径，并将结果进行覆盖
                List<QuerySystemDocTreeByFileidDto> parentDirList = new ArrayList<>();
                Queue<QuerySystemDocTreeByFileidDto> que = new LinkedList<>(queryByFileName);
                Map<Long, QuerySystemDocTreeByFileidDto> queryByOpNameMap = allDirList.stream().collect(Collectors.toMap(QuerySystemDocTreeByFileidDto::getFileid, o -> o));
                while (!que.isEmpty()) {
                    QuerySystemDocTreeByFileidDto peek = que.poll();
                    QuerySystemDocTreeByFileidDto parent = queryByOpNameMap.get(peek.getParent());
                    if (Objects.nonNull(parent)) {
                        que.add(parent);
                        parentDirList.add(parent);
                    }
                }
                //未找到根目录直接返回
                if (CollectionUtil.isEmpty(parentDirList)) {
                    return SystemRelatedFileInfoVo.EMPTY_LIST;
                }
                parentDirList.addAll(queryByFileName);
                allDirList = parentDirList;
            }
        }
        DateTimeFormatter simpleDateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //将结果聚合转换成vo
        List<SystemRelatedFileInfoVo.FileInfoVo> voList = allDirList.stream()
                .distinct()
                .map(q -> SystemRelatedFileInfoVo.FileInfoVo.builder()
                        .fileType(getType(q))
                        .title(q.getName())
                        .key(q.getPath())
                        .path(q.getPath())
                        .isLeaf(!OcFilecacheMimetypeEnum.dir.getCode().equals(q.getMimetype()))
                        .size(getSize(q.getSize()))
                        .createTime(Objects.isNull(q.getStorageMtime()) ? null :
                                simpleDateFormat.format(LocalDateTime.ofInstant(Instant.ofEpochSecond(q.getStorageMtime()), ZoneId.systemDefault())))
                        .lastModifyTime(Objects.isNull(q.getMtime()) ? null :
                                simpleDateFormat.format(LocalDateTime.ofInstant(Instant.ofEpochSecond(q.getMtime()), ZoneId.systemDefault())))
                        .fileid(q.getFileid())
                        .parent(q.getParent())
                        .level(q.getDirLevel())
                        .build())
                .collect(Collectors.toList());
        Map<Long, SystemRelatedFileInfoVo.FileInfoVo> voMap = voList.stream().collect(Collectors.toMap(SystemRelatedFileInfoVo.FileInfoVo::getFileid, o -> o, (o1, o2) -> o1));
        //设置children，并提炼出文件list
        List<SystemRelatedFileInfoVo.FileInfoVo> allFiles = new ArrayList<>();
        Map<String, List<SystemRelatedFileInfoVo.FileInfoVo>> childrenMap = new HashMap<>();
        voList.forEach(v -> {
            if (Objects.nonNull(v.getParent())) {
                SystemRelatedFileInfoVo.FileInfoVo parent = voMap.get(v.getParent());
                if (Objects.isNull(parent)) {
                    v.setParent(null);
                } else {
                    if (v.getIsLeaf()) {
                        SystemRelatedFileInfoVo.FileInfoVo parentTmp = parent;
                        while (parentTmp != null) {
                            List<SystemRelatedFileInfoVo.FileInfoVo> parentChildrenFiles = null == parentTmp.getChildrenFiles() ? new ArrayList<>() : parentTmp.getChildrenFiles();
                            parentChildrenFiles.add(v);
                            parentTmp.setChildrenFiles(parentChildrenFiles);
                            childrenMap.computeIfAbsent(parentTmp.getKey(), k -> new ArrayList<>()).add(v);
                            parentTmp = voMap.get(parentTmp.getParent());
                        }
                    } else {
                        Set<SystemRelatedFileInfoVo.FileInfoVo> childrenDir = null == parent.getChildren() ?
                                new TreeSet<>(Comparator.comparing(SystemRelatedFileInfoVo.FileInfoVo::getTitle)) : parent.getChildren();
                        childrenDir.add(v);
                        parent.setChildren(childrenDir);
                    }
                }
                if (v.getIsLeaf()) {
                    allFiles.add(v);
                }
            }
        });
        return SystemRelatedFileInfoVo.builder()
                .dirs(voList.stream().parallel().filter(v -> Objects.isNull(v.getParent())).sorted(Comparator.comparing(SystemRelatedFileInfoVo.FileInfoVo::getTitle)).collect(Collectors.toList()))
                .allFiles(allFiles.stream().parallel().distinct().sorted(Comparator.comparing(SystemRelatedFileInfoVo.FileInfoVo::getTitle)).collect(Collectors.toList()))
                .childrenMap(childrenMap)
                .build();
    }

    private String getType(QuerySystemDocTreeByFileidDto dto) {
        if (!OcFilecacheMimetypeEnum.dir.getCode().equals(dto.getMimetype())) {
            return Strings.EMPTY;
        }
        // 获取文件名中最后一个"."字符的位置，即扩展名的起始位置
        int index = dto.getName().lastIndexOf(".");
        // 获取扩展名
        return index < 0 ? Strings.EMPTY : dto.getName().substring(index + 1);
    }

    private String getSize(@NonNull Long size) {
        BigDecimal sizeBD = new BigDecimal(size);
        if (sizeBD.compareTo(SIZE_KB) < 0) {
            return sizeBD.setScale(2, RoundingMode.DOWN) + "字节";
        } else if (sizeBD.compareTo(SIZE_MB) < 0) {
            return sizeBD.divide(SIZE_KB).setScale(2, RoundingMode.DOWN) + "KB";
        } else if (sizeBD.compareTo(SIZE_GB) < 0) {
            return sizeBD.divide(SIZE_MB).setScale(2, RoundingMode.DOWN) + "MB";
        } else if (sizeBD.compareTo(SIZE_TB) < 0) {
            return sizeBD.divide(SIZE_GB).setScale(2, RoundingMode.DOWN) + "GB";
        } else {
            return sizeBD.divide(SIZE_TB).setScale(2, RoundingMode.DOWN) + "TB";
        }
    }

    /**
     * 下载
     */
    public ResponseEntity<byte[]> download(@NotBlank String url, HttpServletRequest request, boolean isDir) throws IOException {
        if (url.startsWith("files/")) {
            url = url.replaceFirst("files/", "");
        }
        HttpResponse<byte[]> httpResponse = isDir ? OwncloudUtils.downloadDir(url) : OwncloudUtils.downloadFile(url);
        if (HttpStatus.valueOf(httpResponse.statusCode()).is2xxSuccessful()) {
            java.net.http.HttpHeaders headers = httpResponse.headers();
            org.springframework.http.HttpHeaders springHeaders = new org.springframework.http.HttpHeaders();
            springHeaders.putAll(headers.map());
            return new ResponseEntity<>(httpResponse.body(), springHeaders, HttpStatus.OK);
        } else {
            return new ResponseEntity<>(new byte[]{}, new org.springframework.http.HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 新增文件夹
     */

    public HttpResponse<String> addDirOrFile(String url, MultipartFile file) throws IOException {
        return addDirOrFile(url, file, null);
    }

    public HttpResponse<String> addDirOrFile(String url, MultipartFile file, String comment) throws IOException {
        return addDirOrFile(url, file, comment, true);
    }

    public HttpResponse<String> addDirOrFile(String url, MultipartFile file, String comment, boolean findExist) throws IOException {
        return addDirOrFile(url, file, comment, findExist, false, null);
    }

    public HttpResponse<String> addDirOrFile(String url, MultipartFile file, String comment, boolean findExist, boolean async, Consumer<HttpResponse<String>> rollback) throws IOException {
        if (findExist && exist(Objects.isNull(file) ? url : url + "/" + file.getOriginalFilename())) {
            throw PlatformException.error("已存在该" + (Objects.isNull(file) ? ("文件夹：" + url) : ("文件：" + file.getOriginalFilename())), false, false);
        }
        if (url.startsWith("files/")) {
            url = url.replaceFirst("files/", "");
        }
        HttpResponse<String> response = Objects.isNull(file) ? OwncloudUtils.addDir(url, async, rollback) : OwncloudUtils.addFile(url, file, async, rollback);
        if (HttpStatus.valueOf(response.statusCode()).is2xxSuccessful()) {
            if (Strings.isBlank(comment)) {
                PlatformUserDto account = requestTool.getUserFromRequest();
                comment = "【国联技术服务平台】" + (Objects.nonNull(account) ? account.getName() : "") + "-新建";
            }
            OwncloudUtils.addComment(comment,
                    ("files/" + url + (Objects.isNull(file) ? "" : ("/" + file.getOriginalFilename())))
                            .replaceFirst(
                                    OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL),
                                    OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_DB)));
        }
        return response;
    }

    /**
     * 重命名
     */
    public HttpResponse<String> renameDirOrFile(String oldNamePath, String newNamePath) throws UnsupportedEncodingException {
        return renameDirOrFile(oldNamePath, newNamePath, null);
    }

    public HttpResponse<String> renameDirOrFile(String oldNamePath, String newNamePath, String comment) throws UnsupportedEncodingException {
        if (exist(newNamePath.endsWith("/") ? newNamePath.substring(0, newNamePath.length() - 1) : newNamePath)) {
            throw PlatformException.error("已存在" + (newNamePath.endsWith("/") ? "文件夹" : "文件"), false, false);
        }
        if (oldNamePath.startsWith("files/")) {
            oldNamePath = oldNamePath.replaceFirst("files/", "");
        }
        if (oldNamePath.endsWith("/")) {
            oldNamePath = oldNamePath.substring(0, oldNamePath.length() - 1);
        }
        if (newNamePath.startsWith("files/")) {
            newNamePath = newNamePath.replaceFirst("files/", "");
        }
        if (newNamePath.endsWith("/")) {
            newNamePath = newNamePath.substring(0, newNamePath.length() - 1);
        }
        HttpResponse<String> response = OwncloudUtils.renameDirOrFile(oldNamePath, newNamePath);
        if (HttpStatus.valueOf(response.statusCode()).is2xxSuccessful()) {
            if (Strings.isBlank(comment)) {
                PlatformUserDto account = requestTool.getUserFromRequest();
                comment = "【国联技术服务平台】" + (Objects.nonNull(account) ? account.getName() : "未知人") +
                        "-由《" + oldNamePath.substring(oldNamePath.lastIndexOf("/") + 1) + "》" +
                        "重命名为《" + newNamePath.substring(newNamePath.lastIndexOf("/") + 1) + "》";
            }
            OwncloudUtils.addComment(comment, ("files/" + newNamePath).replaceFirst(
                    OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL),
                    OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_DB)));
        }
        return response;
    }


    /**
     * 删除
     */
    public HttpResponse<String> delDirOrFile(@NotBlank String url) {
        if (url.startsWith("files/")) {
            url = url.replaceFirst("files/", "");
        }
        return OwncloudUtils.delDirOrFile(url);
    }

    private boolean exist(@NotBlank String url) {
        SystemConfig rootConfig = systemConfigService.findByCode(CONFIG_KEY_SYSTEM_DOC_ROOT_FILEID);
        List<Long> rootFileids = Arrays.stream(Optional.ofNullable(rootConfig).orElseThrow(() -> PlatformException.error("网盘浏览异常-缺少配置：系统文档根目录对应owncloud的fileid-" + CONFIG_KEY_SYSTEM_DOC_ROOT_FILEID)).getConfigValue()
                .split(",")).map(Long::valueOf).collect(Collectors.toList());
        //文件所有人
        List<Integer> allStorages = ocFilecacheRepo.findAllByFileidIn(rootFileids).stream().map(OcFilecache::getStorage).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(allStorages)) {
            logger.error("owncloud重复检查：未找到根节点信息");
            return false;
        }
        //处理URL
        final String[] tmpUrl = {url};
        SystemConfig replacePathConfig = systemConfigService.findByCode(CONFIG_KEY_SYSTEM_DOC_PATH_REPLACE);
        if (Objects.nonNull(replacePathConfig) && Strings.isNotBlank(replacePathConfig.getConfigValue())) {
            Map<String, String> replaceMap = Arrays.stream(replacePathConfig.getConfigValue().split(",")).collect(Collectors.toMap(c -> c.split(":")[0], c -> c.split(":")[1]));
            replaceMap.forEach((k, v) -> {
                tmpUrl[0] = tmpUrl[0].replaceFirst(v, k);
            });
        }
        //查找所有人是否有重复路径
        List<OcFilecache> exists = ocFilecacheRepo.findAllByPathAndStorageIn(tmpUrl[0], allStorages);
        return CollectionUtil.isNotEmpty(exists);
    }

    public SystemRelatedFileConfigVo getSystemDirConfig() {
        List<OwnCloudSystemDirConfig> allConfig = systemDirConfigRepo.findAll();
        //当前配置
        List<SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo> lastConfigVo = buildConfigVo(allConfig.stream().filter(c -> Objects.isNull(c.getVersionTime())).collect(Collectors.toList()));
        //系统节点
        SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo sysVo = SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo.builder()
                .title("系统")
                .children(lastConfigVo.stream().parallel()
                        .filter(c -> Objects.isNull(c.getParentId()))
                        .sorted(Comparator.comparing(SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo::getTitle))
                        .collect(Collectors.toCollection(LinkedHashSet::new)))
                .key(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_3RD)
                .path(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_3RD)
                .parentPath(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_2ND)
                .sysInsideDir(false)
                .build();
        //系统小类节点
        SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo sysSecClzVo = SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo.builder()
                .title("系统类别")
                .children(new HashSet<>() {{
                    add(sysVo);
                }})
                .key(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_2ND)
                .path(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_2ND)
                .parentPath(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_1ST)
                .canMoveIn(false)
                .sysInsideDir(false)
                .build();
        //系统大类节点
        SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo sysMainClzVo = SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo.builder()
                .title("系统大类")
                .children(new HashSet<>() {{
                    add(sysSecClzVo);
                }})
                .key(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_1ST)
                .path(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_1ST)
                .canMoveIn(false)
                .sysInsideDir(false)
                .build();
        Map<LocalDateTime, List<OwnCloudSystemDirConfig>> records = allConfig.stream()
                .filter(c -> Objects.nonNull(c.getVersionTime()))
                .collect(Collectors.groupingBy(OwnCloudSystemDirConfig::getVersionTime));
        Map<String, List<SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo>> recordsVo = records.entrySet().stream()
                .map(entry -> new AbstractMap.SimpleEntry<>(entry.getKey().toString(), buildConfigVo(entry.getValue())))
                .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
        return SystemRelatedFileConfigVo.builder()
                .lastConfig(List.of(sysMainClzVo))
                .lastConfigPathSet(allConfig.stream().map(OwnCloudSystemDirConfig::getPath).collect(Collectors.toSet()))
                .records(recordsVo)
                .build();
    }

    private static List<SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo> buildConfigVo(List<OwnCloudSystemDirConfig> lastConfig) {
        List<SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo> lastConfigVo = lastConfig.stream()
                .map(c -> {
                    SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo vo = SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo.convertFrom(c);
                    vo.setPath(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_3RD + (Strings.isBlank(c.getPath()) ? "" : c.getPath()));
                    vo.setParentPath(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_3RD + (Strings.isBlank(c.getParentPath()) ? "" : c.getParentPath()));
                    vo.setKey(vo.getPath());
                    return vo;
                })
                .sorted(Comparator.comparing(SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo::getTitle))
                .collect(Collectors.toList());
        Map<Long, List<SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo>> childrenMap = lastConfigVo.stream()
                .filter(c -> Objects.nonNull(c.getParentId()))
                .collect(Collectors.groupingBy(SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo::getParentId));
        lastConfigVo.forEach(c -> {
            if (childrenMap.containsKey(c.getFileid())) {
                c.setChildren(new LinkedHashSet<>(childrenMap.get(c.getFileid())));
            }
        });
        return lastConfigVo;
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateSystemDirConfig(List<SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo> configList) {
        if (CollectionUtils.isEmpty(configList)) {
            throw PlatformException.error("不能配置空列表", false, false);
        }
        if (!ibltRedis.lock(RedisKey.GATEAY_OWNCLOUD_SYNC)) {
            logger.info("endOaFlow error：redis抢锁失败， key-" + RedisKey.GATEAY_OWNCLOUD_SYNC);
            throw PlatformException.error("有网盘任务正在运行，请稍后操作", false, false);
        }
        try {
            String operator = requestTool.getAccountFromRequest();
            //先标记现有最新的版本时间
            systemDirConfigRepo.setVersionTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), operator);
            Map<String, OwnCloudSystemDirConfig> pathMap = new ConcurrentHashMap<>();
            //BFS遍历，此处不能开启多线程
            TreeNodeUtils.BFSWalk(configList.get(0), root -> CollectionUtil.isNotEmpty(root.getChildren()) ? new ArrayList<>(root.getChildren()) : Collections.EMPTY_LIST, (root) -> {
                if (!PRE_DIR.contains(root.getPath())) {
                    OwnCloudSystemDirConfig parent = pathMap.getOrDefault(root.getParentPath().replaceFirst(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_3RD, ""), null);
                    OwnCloudSystemDirConfig save = systemDirConfigRepo.save(
                            OwnCloudSystemDirConfig.builder()
                                    .parentId(Objects.nonNull(parent) ? parent.getId() : null)
                                    .name(root.getTitle())
                                    .parentName(Objects.nonNull(parent) ? parent.getName() : null)
                                    .parentPath(Objects.nonNull(parent) ? parent.getPath() : null)
                                    .path(root.getPath().replaceFirst(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_3RD, ""))
                                    .valid(false)
                                    .createAccount(operator)
                                    .build()
                    );
                    pathMap.put(save.getPath(), save);
                }
            });
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        } finally {
            ibltRedis.unlock(RedisKey.GATEAY_OWNCLOUD_SYNC);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void validConfig(@NonNull Boolean valid) {
        if (!ibltRedis.lock(RedisKey.GATEAY_OWNCLOUD_SYNC)) {
            logger.info("endOaFlow error：redis抢锁失败， key-" + RedisKey.GATEAY_OWNCLOUD_SYNC);
            throw PlatformException.error("有网盘任务正在运行，请稍后操作", false, false);
        }
        try {
            //将最新配置标记生效
            List<OwnCloudSystemDirConfig> lastConfigs = systemDirConfigRepo.findAllByVersionTimeNull();
            if (Objects.isNull(lastConfigs)) {
                throw PlatformException.error("未找到最新配置信息", false, false);
            }
            lastConfigs.forEach(c -> {
                c.setValid(valid);
                c.setUpdateAccount(requestTool.getAccountFromRequest());
            });
            systemDirConfigRepo.saveAll(lastConfigs);
        } finally {
            ibltRedis.unlock(RedisKey.GATEAY_OWNCLOUD_SYNC);
        }
    }

    public Boolean getValidConfig() {
        List<OwnCloudSystemDirConfig> lastConfigs = systemDirConfigRepo.findAllByVersionTimeNull();
        return CollectionUtil.isNotEmpty(lastConfigs)
                && new ArrayList<>(lastConfigs.stream().collect(Collectors.groupingBy(OwnCloudSystemDirConfig::isValid)).keySet()).get(0);
    }

    /**
     * 检查网盘没有的系统
     */
    public List<String> checkUnMatchDir() {
        //找到所有系统
        List<SystemMain> allSystem = systemMainService.queryAll();
        if (CollectionUtil.isEmpty(allSystem)) {
            throw PlatformException.error("当前无系统", false, false);
        }
        //根目录文件所有人
        SystemConfig rootConfig = systemConfigService.findByCode(CONFIG_KEY_SYSTEM_DOC_ROOT_FILEID);
        List<Long> rootFileids = Arrays.stream(Optional.ofNullable(rootConfig).orElseThrow(() -> PlatformException.error("网盘浏览异常-缺少配置：系统文档根目录对应owncloud的fileid-" + CONFIG_KEY_SYSTEM_DOC_ROOT_FILEID)).getConfigValue()
                .split(",")).map(Long::valueOf).collect(Collectors.toList());
        List<Integer> allStorages = ocFilecacheRepo.findAllByFileidIn(rootFileids).stream().map(OcFilecache::getStorage).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(allStorages)) {
            throw PlatformException.error("owncloud重复检查：未找到根节点信息", false, false);
        }
        //Map<dbPath, opName>根据系统名转换成路径
        Map<String, String> allSystemPathMap = allSystem.stream()
                .collect(Collectors.toMap(s -> getPathByOpName(s, OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_DB))
                        , SystemMain::getOpName, (o1, o2) -> o1, LinkedHashMap::new));
        //查找网盘DB库所有路径
        List<OcFilecache> allOwnCloudSystemDirs = ocFilecacheRepo.findAllByPathInAndStorageIn(new ArrayList<>(allSystemPathMap.keySet()), allStorages);
        if (CollectionUtil.isEmpty(allOwnCloudSystemDirs)) {
            return new ArrayList<>(allSystemPathMap.values());
        }
        Set<String> allOwnCloudSystemPathSet = allOwnCloudSystemDirs.stream().map(OcFilecache::getPath).collect(Collectors.toSet());
        //逐个匹配，不存在文件的挑出
        return allSystemPathMap.entrySet().stream()
                .filter(entry -> !allOwnCloudSystemPathSet.contains(entry.getKey()))
                .map(Map.Entry::getValue)
                .collect(Collectors.toList());
    }


    /**
     * 重命名系统文件夹
     *
     * @return
     */
    public HttpResponse<String> renameSysDir(SystemMain oldSysMain, SystemMain newSysMain, String comment) throws UnsupportedEncodingException {
        String oldPath = getPathByOpName(oldSysMain, OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL));
        //新名称可能没有落库，需要手动生成
        String newPath = getPathByOpName(newSysMain, OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL));
        return renameDirOrFile(oldPath, newPath, comment);
    }

    private String getPathByOpName(SystemMain systemMain, String prePath) {
        //所有系统类别
        ItDict systemMainClass = dictionaryService.findByDictEntryAndDictSubentry("system_main_class", systemMain.getMainClass());
        if (Objects.isNull(systemMainClass)) {
            throw PlatformException.error("获取系统路径错误：未找到系统类别" + systemMain.getMainClass(), false, false);
        }
        //所有系统小类
        ItDict systemSecondClassUnion = dictionaryService.findByDictEntryAndDictSubentry("system_second_class_union", systemMain.getSystemClass());
        if (Objects.isNull(systemSecondClassUnion)) {
            throw PlatformException.error("获取系统路径错误：未找到系统小类" + systemMain.getSystemClass(), false, false);
        }
        return prePath + systemMainClass.getDictSubentry() + " " + systemMainClass.getDictValue() + "/" +
                systemSecondClassUnion.getDictSubentry() + " " + systemSecondClassUnion.getDictValue() + "/" +
                systemMain.getSystemClassNo() + " " + systemMain.getOpName();
    }

    /**
     * 根据配置信息生成系统文件目录
     *
     * @return
     */
    public void matchDir(List<String> opNameList) {
        matchDir(opNameList, null);
    }

    public void matchDir(List<String> opNameList, String comment) {
        if (CollectionUtil.isEmpty(opNameList)) {
            throw PlatformException.error("暂无系统", false, false);
        }
        if (!getValidConfig()) {
            logger.error("当前配置未生效");
            return;
        }
        if (!ibltRedis.lock(RedisKey.GATEAY_OWNCLOUD_SYNC, "初始化...", 6000)) {
            logger.info("endOaFlow error：redis抢锁失败， key-" + RedisKey.GATEAY_OWNCLOUD_SYNC);
            throw PlatformException.error("有网盘任务正在运行，请稍后操作", false, false);
        }
        //异步
        new Thread(() -> {
            LocalDateTime begin = LocalDateTime.now();
            try {
                //查找配置
                SystemRelatedFileConfigVo config = getSystemDirConfig();
                //查找系统
                List<SystemMain> systemMainList = systemMainService.query(SystemFlowStatisticsSearchParamDto.builder().opNameList(opNameList).build());
                //所有系统类别
                Map<String, String> systemMainClassMap = dictionaryService.findByDictData("system_main_class").getDictValues().stream().collect(Collectors.toMap(ItDict::getDictSubentry, ItDict::getDictValue));
                //所有系统小类
                Map<String, String> systemSecondClassMap = dictionaryService.findByDictData("system_second_class_union").getDictValues().stream().collect(Collectors.toMap(ItDict::getDictSubentry, ItDict::getDictValue));
                try {
                    //新增系统最外层目录
                    addDirOrFile(OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL), null);
                    // 删除临时基础项目路径
                    logger.info("删除临时目录");
                    delDirOrFile(OWNCLOUD_SYSTEM_DOC_SYS_DIR_PATH_TMP);
                    //根据最新的配置新建【临时系统目录】
                    addDirOrFile(OWNCLOUD_SYSTEM_DOC_SYS_DIR_PATH_TMP, null, comment);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                ibltRedis.set(RedisKey.GATEAY_OWNCLOUD_SYNC, "创建基础目录...");
                // BFS遍历创建临时基础项目路径
                try {
                    TreeNodeUtils.BFSWalk(config.getLastConfig().get(0), root -> CollectionUtil.isNotEmpty(root.getChildren()) ? new ArrayList<>(root.getChildren()) : Collections.EMPTY_LIST,
                            (c) -> {
                                ibltRedis.expire(RedisKey.GATEAY_OWNCLOUD_SYNC, 6000);
                                if (!PRE_DIR.contains(c.getPath())) {
                                    //如果没有则新建配置好的文件夹
                                    try {
                                        addDirOrFile(OWNCLOUD_SYSTEM_DOC_SYS_DIR_PATH_TMP + c.getPath().replaceFirst(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_3RD, ""), null, comment, false);
                                    } catch (PlatformException e) {
                                        logger.error(e.getMsg());
                                    } catch (IOException e) {
                                        throw new RuntimeException(e);
                                    }
                                }
                            }
                            , true);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException(e);
                }
                logger.info("临时目录创建完毕");
                ibltRedis.set(RedisKey.GATEAY_OWNCLOUD_SYNC, "基础目录创建完毕...，0%");
                //对模板路径遍历复制粘贴到系统文件夹中
                List<SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo> lastConfig = config.getLastConfig();
                //找到第一个名字不带系统路径的
                while (CollectionUtil.isNotEmpty(lastConfig) && PRE_DIR.contains(lastConfig.get(0).getPath())) {
                    lastConfig = new ArrayList<>(lastConfig.get(0).getChildren());
                }
                List<SystemRelatedFileConfigVo.SystemRelatedFileConfigDetailVo> finalLastConfig = lastConfig;
                //Map<系统大类, Map<系统小类, List<系统>>>
                List<SystemMain> filterSysList = systemMainList.parallelStream()
                        .filter(sys -> {
                            if (!systemMainClassMap.containsKey(sys.getMainClass())) {
                                logger.error("未找到系统大类-{}", sys.getMainClass());
                                return false;
                            }
                            if (!systemSecondClassMap.containsKey(sys.getSystemClass())) {
                                logger.error("未找到系统小类-{}", sys.getSystemClass());
                                return false;
                            }
                            if (SYSTEM_OPNAME_MATCH_REG_EX.matcher(sys.getOpName()).find()) {
                                logger.info("创建系统目录失败，系统名称，{}，不能包含除（._-[]()<>）外的特殊符号", sys);
                                return false;
                            }
                            return true;
                        })
                        .collect(Collectors.toList());
                AtomicStampedReference<Integer> count = new AtomicStampedReference<>(0, 0);
                Integer countAll = lastConfig.size() * filterSysList.size();
                Integer configSize = finalLastConfig.size();
                ibltRedis.set(RedisKey.GATEAY_OWNCLOUD_SYNC, "开始同步目录，共" + countAll + "个，0%");
                Map<String, Integer> processPercentMap = filterSysList.stream().collect(Collectors.toMap(s -> s.getSystemClassNo() + " " + s.getOpName(), c -> 0, (o1, o2) -> o1, ConcurrentHashMap::new));
                Set<String> mainClzDirAdd = new ConcurrentHashSet<>();
                Set<String> secondClzDirAdd = new ConcurrentHashSet<>();
                filterSysList.parallelStream().forEach(sys -> {
                    ocThreadPool.execute(() -> {
                        ibltRedis.set(RedisKey.GATEAY_OWNCLOUD_SYS_SYNC + sys.getSystemClassNo(), "准备同步...，" + "0/" + configSize);
                        String mainClzDirName = sys.getMainClass() + " " + systemMainClassMap.get(sys.getMainClass());
                        String secondClzDirName = sys.getSystemClass() + " " + systemSecondClassMap.get(sys.getSystemClass());
                        String sysDirName = sys.getSystemClassNo() + " " + sys.getOpName();
                        if (!mainClzDirAdd.contains(mainClzDirName)) {
                            try {
                                //如果没有则新建【系统大类文件夹】
                                addDirOrFile(OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL) + mainClzDirName, null);
                                mainClzDirAdd.add(mainClzDirName);
                            } catch (PlatformException e) {
                                logger.error(e.getMsg());
                            } catch (IOException e) {
                                logger.error(e.getMessage());
                            }
                        }
                        if (!secondClzDirAdd.contains(secondClzDirName)) {
                            try {
                                //如果没有则新建【系统小类文件夹】
                                addDirOrFile(OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL) + mainClzDirName + "/" + secondClzDirName, null);
                                secondClzDirAdd.add(secondClzDirName);
                            } catch (PlatformException e) {
                                logger.error(e.getMsg());
                            } catch (IOException e) {
                                logger.error(e.getMessage());
                            }
                        }
                        ocThreadPool.execute(() -> {
                            try {

                                //如果没有则新建【系统文件夹】
                                addDirOrFile(OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL) + mainClzDirName + "/" + secondClzDirName + "/" + sysDirName, null, comment, true, false,
                                        fileResp -> {
                                            ibltRedis.expire(RedisKey.GATEAY_OWNCLOUD_SYNC, 6000);
                                            finalLastConfig.forEach(c -> {
                                                if (!PRE_DIR.contains(c.getPath())) {
                                                    String configPath = c.getPath().replaceFirst(OWNCLOUD_SYSTEM_DOC_DIR_CONFIG_PRE_PATH_3RD, "");
                                                    try {
                                                        logger.info(OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL).replaceFirst("files/", "") + mainClzDirName + "/" + secondClzDirName + "/" + sysDirName + configPath);
                                                        OwncloudUtils.parseTo(OWNCLOUD_SYSTEM_DOC_SYS_DIR_PATH_TMP + configPath,
                                                                OwncloudUtils.getConfig(OWNCLOUD_SYSTEM_DOC_DIR_PATH_PRE_URL).replaceFirst("files/", "") + mainClzDirName + "/" + secondClzDirName + "/" + sysDirName + configPath
                                                                , true
                                                                , resp -> {
                                                                    logger.info("创建系统目录完成：{}, {}, {}, {}", mainClzDirName + "/" + secondClzDirName + "/" + sysDirName, c.getPath(), resp.statusCode(), resp.body());
                                                                    while (!count.compareAndSet(count.getReference(), count.getReference() + 1, count.getStamp(), count.getStamp() + 1))
                                                                        ;
                                                                    if (count.getReference() < countAll) {
                                                                        //每次成功都续锁，直到最后一个
                                                                        ibltRedis.set(RedisKey.GATEAY_OWNCLOUD_SYNC, "同步目录中，剩余" + (countAll - count.getReference()) + "个，"
                                                                                + (BigDecimal.valueOf(count.getReference() * 100).divide(BigDecimal.valueOf(countAll), 2, RoundingMode.HALF_DOWN)) + "%");
                                                                        ibltRedis.expire(RedisKey.GATEAY_OWNCLOUD_SYNC, 6000);
                                                                    } else {
                                                                        ibltRedis.set(RedisKey.GATEAY_OWNCLOUD_SYNC, "已完成同步目录，100%");
                                                                        ibltRedis.unlock(RedisKey.GATEAY_OWNCLOUD_SYNC);
                                                                        Duration between = Duration.between(LocalDateTime.now(), begin);
                                                                        logger.info("网盘同步结束，{}个系统，调用耗时：{} 分，{} 秒", opNameList.size(), between.toMinutes(), between.toSeconds());
                                                                    }
                                                                    Integer perc = processPercentMap.get(sys.getSystemClassNo() + " " + sys.getOpName()) + 1;
                                                                    processPercentMap.put(sys.getSystemClassNo() + " " + sys.getOpName(), perc);
                                                                    ibltRedis.set(RedisKey.GATEAY_OWNCLOUD_SYS_SYNC + sys.getSystemClassNo(), "同步目录中，" + perc + "/" + configSize);
                                                                });
                                                    } catch (UnsupportedEncodingException e) {
                                                        logger.error("创建系统目录失败：{}, {}, {}", mainClzDirName + "/" + secondClzDirName + "/" + sysDirName, c.getPath(), e.getMessage());
                                                    }
                                                }
                                            });
                                        });
                            } catch (PlatformException e) {
                                logger.error(e.getMsg());
                            } catch (IOException e) {
                                logger.error(e.getMessage());
                            }
                        });
                    });
                });
            } finally{
                Duration between = Duration.between(LocalDateTime.now(), begin);
                logger.info("网盘同步结束，{}个系统，调用耗时：{} 分，{} 秒， 等待后续异步调用结果", opNameList.size(), between.toMinutes(), between.toSeconds());
            }
        }).start();
    }
            @Data
            @AllArgsConstructor
            @NoArgsConstructor
            @Builder
            public static class MatchDirResDto {
                private int suc;
                private int failed;
                private String failedMsg;
            }
        }
